import requests
import math
from datetime import datetime

class CryptoAnalyzer:
    def __init__(self):
        self.symbols = ['BTCUSDT', 'ETHUSDT']
        self.historical_data = {}
        self.current_signals = {}
        self.backtester = Backtester()
        
        for symbol in self.symbols:
            self.historical_data[symbol] = {}
            self.current_signals[symbol] = {}
    
    def fetch_historical_data(self, symbol, interval, limit=500):
        try:           
            # [[1754614740000,"117281.40","117299.20","117232.40","117232.40","29.251",1754614799999,"3429748.27580",1220,"15.917","1866239.10370","0"]]
            # 开盘时间：2025-08-08 09:02:34 (北京时间)
            # 开盘价：117281.40 USDT
            # 最高价：117299.20 USDT
            # 最低价：117232.40 USDT
            # 收盘价：117232.40 USDT
            # 成交量：29.251 BTC
            # 收盘时间：2025-08-08 09:03:34 (北京时间)
            # 成交额：3429748.27580 USDT
            # 成交笔数：1220笔
            # 主动买入成交量：15.917 BTC
            # 主动买入成交额：1866239.10370 USDT
            # 忽略值：0

            url = f"https://bnapi.bnlex.top/fapi/fapi/v1/klines?symbol={symbol}&interval={interval}&limit={limit}"
            response = requests.get(url)
            data = response.json()
            
            return [
                {
                    'time': math.floor(item[0] / 1000),
                    'open': float(item[1]),
                    'high': float(item[2]),
                    'low': float(item[3]),
                    'close': float(item[4]),
                    'volume': float(item[5]),
                    'closeTime': item[6]
                }
                for item in data
            ]
        except Exception as e:
            print(f"获取{symbol}历史数据失败: {e}")
            return None
    
    def convert_to_higher_timeframe(self, data, factor):
        converted = []
        for i in range(0, len(data) - factor + 1, factor):
            chunk = data[i:i+factor]
            new_candle = {
                'time': chunk[0]['time'],
                'open': chunk[0]['open'],
                'high': max(c['high'] for c in chunk),
                'low': min(c['low'] for c in chunk),
                'close': chunk[-1]['close'],
                'volume': sum(c['volume'] for c in chunk),
                'closeTime': chunk[-1]['closeTime']
            }
            converted.append(new_candle)
        return converted
    
    async def fetch_data_for_all_symbols(self, interval):
        for symbol in self.symbols:
            fetch_interval = interval
            if interval == '10m':
                fetch_interval = '5m'
            elif interval == '30m':
                fetch_interval = '15m'
                
            raw_data = self.fetch_historical_data(symbol, fetch_interval)
            if not raw_data:
                continue
                
            if interval in ['10m', '30m']:
                data = self.convert_to_higher_timeframe(raw_data, 2)
            else:
                data = raw_data
                
            self.historical_data[symbol][interval] = data
    
    def calculate_rsi(self, data, period=14):
        if len(data) < period + 1:
            return [None] * len(data)
        
        rsi = [None] * period
        avg_gain = 0.0
        avg_loss = 0.0
        
        for i in range(1, period + 1):
            change = data[i]['close'] - data[i-1]['close']
            if change > 0:
                avg_gain += change
            else:
                avg_loss += abs(change)
        
        avg_gain /= period
        avg_loss /= period
        
        rs = avg_gain / avg_loss if avg_loss != 0 else 100
        rsi.append(100 - (100 / (1 + rs)))
        
        for i in range(period + 1, len(data)):
            change = data[i]['close'] - data[i-1]['close']
            gain = change if change > 0 else 0
            loss = abs(change) if change < 0 else 0
            
            avg_gain = (avg_gain * (period - 1) + gain) / period
            avg_loss = (avg_loss * (period - 1) + loss) / period
            
            rs = avg_gain / avg_loss if avg_loss != 0 else 100
            rsi.append(100 - (100 / (1 + rs)))
        
        return rsi
    
    def calculate_ema(self, data, period):
        ema = [None] * (period - 1)
        if len(data) < period:
            return ema
        
        sum_close = sum(d['close'] for d in data[:period])
        ema.append(sum_close / period)
        
        multiplier = 2 / (period + 1)
        for i in range(period, len(data)):
            current_ema = (data[i]['close'] - ema[i-1]) * multiplier + ema[i-1]
            ema.append(current_ema)
        
        return ema
    
    def calculate_macd(self, data, short_period=12, long_period=26, signal_period=9):
        if len(data) < long_period + signal_period:
            return {
                'dif': [None] * len(data),
                'dea': [None] * len(data),
                'macd': [None] * len(data)
            }
        
        short_ema = self.calculate_ema(data, short_period)
        long_ema = self.calculate_ema(data, long_period)
        
        dif = []
        for s, l in zip(short_ema, long_ema):
            if s is not None and l is not None:
                dif.append(s - l)
            else:
                dif.append(None)
        
        dea_data = [{'close': d if d is not None else 0, 'time': data[i]['time']} 
                   for i, d in enumerate(dif)]
        dea = self.calculate_ema(dea_data, signal_period)
        
        macd = []
        for d, s in zip(dif, dea):
            if d is not None and s is not None:
                macd.append((d - s) * 2)
            else:
                macd.append(None)
        
        return {'dif': dif, 'dea': dea, 'macd': macd}
    
    def calculate_kdj(self, data, period=9, k_period=3, d_period=3):
        if len(data) < period + d_period:
            return {
                'k': [None] * len(data),
                'd': [None] * len(data),
                'j': [None] * len(data)
            }
        
        k_values = []
        d_values = []
        j_values = []
        
        for i in range(len(data)):
            if i < period - 1:
                k_values.append(None)
                d_values.append(None)
                j_values.append(None)
                continue
            
            high = max(data[j]['high'] for j in range(i - period + 1, i + 1))
            low = min(data[j]['low'] for j in range(i - period + 1, i + 1))
            close = data[i]['close']
            
            if high == low:
                rsv = 0
            else:
                rsv = (close - low) / (high - low) * 100
            
            if k_values[i-1] is not None:
                k = (k_values[i-1] * (k_period - 1) + rsv) / k_period
            else:
                k = rsv
            
            if d_values[i-1] is not None:
                d = (d_values[i-1] * (d_period - 1) + k) / d_period
            else:
                d = k
            
            j = 3 * k - 2 * d
            
            k_values.append(k)
            d_values.append(d)
            j_values.append(j)
        
        return {'k': k_values, 'd': d_values, 'j': j_values}
    
    def calculate_ma(self, data, period):
        ma = []
        for i in range(len(data)):
            if i < period - 1:
                ma.append(None)
                continue
            
            sum_close = sum(data[j]['close'] for j in range(i - period + 1, i + 1))
            ma.append(sum_close / period)
        
        return ma
    
    def get_trend(self, values, index):
        if index < 2 or values[index] is None or values[index-1] is None:
            return 'neutral'
        return 'up' if values[index] > values[index-1] else 'down'
    
    def get_macd_trend(self, macd, index):
        if index < 2 or macd['dif'][index] is None or macd['dea'][index] is None:
            return 'neutral'
        
        dif = macd['dif'][index]
        dea = macd['dea'][index]
        prev_dif = macd['dif'][index-1]
        prev_dea = macd['dea'][index-1]
        
        macd_above = dif > dea
        macd_prev_above = prev_dif > prev_dea
        macd_increasing = dif > prev_dif
        dea_increasing = dea > prev_dea
        
        if macd_above:
            if not macd_prev_above:
                return 'crossUp'
            return 'strongUp' if macd_increasing and dea_increasing else 'up'
        else:
            if macd_prev_above:
                return 'crossDown'
            return 'strongDown' if not macd_increasing and not dea_increasing else 'down'
    
    def get_kdj_cross(self, kdj, index):
        if index < 2 or kdj['k'][index] is None or kdj['d'][index] is None:
            return 'neutral'
        
        k = kdj['k'][index]
        d = kdj['d'][index]
        prev_k = kdj['k'][index-1]
        prev_d = kdj['d'][index-1]
        
        if k > d and prev_k < prev_d:
            return 'goldenCross'
        if k < d and prev_k > prev_d:
            return 'deathCross'
        if k > d:
            return 'above'
        if k < d:
            return 'below'
        return 'neutral'
    
    def detect_trend(self, signals):
        # 定义指标权重
        ma_weight = 0.3
        macd_weight = 0.4
        kdj_weight = 0.3
        
        # 计算各指标得分
        ma_score = 0.0
        macd_score = 0.0
        kdj_score = 0.0
        
        # MA趋势得分
        if signals['ma5Trend'] == 'up' and signals['ma10Trend'] == 'up':
            ma_score = 1.0
        elif signals['ma5Trend'] == 'down' and signals['ma10Trend'] == 'down':
            ma_score = -1.0
        
        # MACD趋势得分
        if signals['macdTrend'] in ['crossUp', 'strongUp']:
            macd_score = 1.0
        elif signals['macdTrend'] in ['crossDown', 'strongDown']:
            macd_score = -1.0
        
        # KDJ交叉得分
        if signals['kdjCross'] == 'goldenCross':
            kdj_score = 1.0
        elif signals['kdjCross'] == 'deathCross':
            kdj_score = -1.0
        
        # 计算综合得分
        total_score = ma_score * ma_weight + macd_score * macd_weight + kdj_score * kdj_weight
        
        # 根据综合得分确定方向和置信度
        if total_score > 0.2:
            return {
                'direction': 'up',
                'confidence': min(0.9, abs(total_score) + 0.3),  # 确保置信度在合理范围内
                'type': 'trend',
                'rsi': signals['rsi'],
                'macdTrend': signals['macdTrend']
            }
        elif total_score < -0.2:
            return {
                'direction': 'down',
                'confidence': min(0.9, abs(total_score) + 0.3),  # 确保置信度在合理范围内
                'type': 'trend',
                'rsi': signals['rsi'],
                'macdTrend': signals['macdTrend']
            }
        
        return {
            'direction': 'neutral',
            'confidence': 1.0 - abs(total_score),  # 中性情况的置信度
            'type': 'trend',
            'rsi': signals['rsi'],
            'macdTrend': signals['macdTrend']
        }
    
    def detect_reversal(self, signals):
        # 定义反转信号的权重
        rsi_weight = 0.4
        macd_weight = 0.3
        volume_weight = 0.3
        
        # 计算各指标得分
        rsi_score = 0.0
        macd_score = 0.0
        volume_score = 0.0
        
        # RSI极值得分
        if signals['rsi'] > 80:
            rsi_score = 1.0  # 强烈超买，可能反转
        elif signals['rsi'] > 70:
            rsi_score = 0.7  # 超买，可能反转
        elif signals['rsi'] < 20:
            rsi_score = -1.0  # 强烈超卖，可能反转
        elif signals['rsi'] < 30:
            rsi_score = -0.7  # 超卖，可能反转
        
        # MACD趋势得分
        if signals['macdTrend'] == 'crossDown':
            macd_score = 1.0  # 死叉，可能下跌反转
        elif signals['macdTrend'] == 'crossUp':
            macd_score = -1.0  # 金叉，可能上涨反转
        
        # 成交量激增得分
        if signals['volume']['current'] > signals['volume']['ma20'] * 2.0:
            volume_score = 1.0  # 成交量极大激增
        elif signals['volume']['current'] > signals['volume']['ma20'] * 1.5:
            volume_score = 0.7  # 成交量较大激增
        
        # 顶部反转条件
        if (signals['rsi'] > 70 and signals['rsiTrend'] == 'down' and
            signals['macdTrend'] == 'crossDown' and volume_score > 0):
            # 计算综合得分
            total_score = (rsi_score * rsi_weight + 
                          macd_score * macd_weight + 
                          volume_score * volume_weight) / (rsi_weight + macd_weight + volume_weight)
            
            return {
                'direction': 'down',
                'confidence': min(0.8, abs(total_score) + 0.2),  # 确保置信度在合理范围内
                'type': 'reversal',
                'rsi': signals['rsi'],
                'macdTrend': signals['macdTrend']
            }
        
        # 底部反转条件
        if (signals['rsi'] < 30 and signals['rsiTrend'] == 'up' and
            signals['macdTrend'] == 'crossUp' and volume_score > 0):
            # 计算综合得分
            total_score = (abs(rsi_score) * rsi_weight + 
                          abs(macd_score) * macd_weight + 
                          volume_score * volume_weight) / (rsi_weight + macd_weight + volume_weight)
            
            return {
                'direction': 'up',
                'confidence': min(0.8, abs(total_score) + 0.2),  # 确保置信度在合理范围内
                'type': 'reversal',
                'rsi': signals['rsi'],
                'macdTrend': signals['macdTrend']
            }
        
        return None
    
    def combine_signals(self, trend, reversal, signals):
        if trend['confidence'] >= 0.7:
            return trend
        
        if reversal and self.volume_confirms(reversal, signals):
            return reversal
        
        return trend
    
    def volume_confirms(self, signal, signals):
        volume_trend_match = (signal['direction'] == 'up' and signals['volume']['trend'] == 'up') or \
                            (signal['direction'] == 'down' and signals['volume']['trend'] == 'down')
        return volume_trend_match and signals['volume']['current'] > signals['volume']['ma20']
    
    def generate_prediction(self, signals):
        trend_signal = self.detect_trend(signals)
        reversal_signal = self.detect_reversal(signals)
        return self.combine_signals(trend_signal, reversal_signal, signals)
    
    def calculate_signals(self, data):
        last_index = len(data) - 1
        if last_index < 0:
            return None
        
        ma5 = self.calculate_ma(data, 5)
        ma10 = self.calculate_ma(data, 10)
        ma20 = self.calculate_ma(data, 20)
        rsi = self.calculate_rsi(data, 14)
        macd = self.calculate_macd(data)
        kdj = self.calculate_kdj(data)
        
        ma5_trend = self.get_trend(ma5, last_index)
        ma10_trend = self.get_trend(ma10, last_index)
        ma20_trend = self.get_trend(ma20, last_index)
        rsi_trend = self.get_trend(rsi, last_index)
        macd_trend = self.get_macd_trend(macd, last_index)
        kdj_cross = self.get_kdj_cross(kdj, last_index)
        
        volumes = [d['volume'] for d in data]
        volume_ma20_data = [{'close': d['volume']} for d in data]
        volume_ma20 = self.calculate_ma(volume_ma20_data, 20)[last_index]
        volume_trend = self.get_trend(volumes, last_index)
        
        prediction = self.generate_prediction({
            'ma5Trend': ma5_trend,
            'ma10Trend': ma10_trend,
            'ma20Trend': ma20_trend,
            'rsi': rsi[last_index],
            'rsiTrend': rsi_trend,
            'macdDiff': macd['dif'][last_index] - macd['dea'][last_index] if macd['dif'][last_index] and macd['dea'][last_index] else 0,
            'macdTrend': macd_trend,
            'kdjCross': kdj_cross,
            'volume': {
                'current': volumes[last_index],
                'ma20': volume_ma20,
                'trend': volume_trend
            }
        })
        
        return {
            'indicators': {
                'ma5': ma5[last_index],
                'ma10': ma10[last_index],
                'ma20': ma20[last_index],
                'rsi': rsi[last_index],
                'macd': macd,
                'kdj': kdj
            },
            'prediction': prediction
        }
    
    def get_rsi_level_description(self, rsi_value):
        if rsi_value is None:
            return 'N/A'
        if rsi_value > 80:
            return '极端超买'
        if rsi_value > 70:
            return '超买'
        if rsi_value < 20:
            return '极端超卖'
        if rsi_value < 30:
            return '超卖'
        return '中性'
    
    def get_macd_description(self, macd_trend):
        descriptions = {
            'strongUp': 'MACD指标呈强劲上升趋势',
            'crossUp': 'MACD指标形成金叉',
            'strongDown': 'MACD指标呈强劲下降趋势',
            'crossDown': 'MACD指标形成死叉',
            'neutral': 'MACD指标呈中性'
        }
        return descriptions.get(macd_trend, 'MACD指标无明显趋势')
    
    def get_operation_suggestion(self, prediction):
        # 简化操作建议逻辑
        high_confidence = prediction['confidence'] >= 0.65
        
        if prediction['direction'] == 'up':
            return '买入' if high_confidence else '卖出'
        elif prediction['direction'] == 'down':
            return '卖出' if high_confidence else '买入'
        else:
            return '买入' if prediction['confidence'] >= 0.5 else '卖出'
    
    def get_trend_arrow(self, direction):
        arrows = {'up': '↗', 'down': '↘', 'neutral': '→'}
        return arrows.get(direction, '→')
    
    def analyze_current_data(self, symbol, interval):
        data = self.historical_data.get(symbol, {}).get(interval)
        if not data or len(data) < 100:
            return None
        
        signals = self.calculate_signals(data)
        if not signals or not signals['prediction']:
            return None
        
        self.current_signals[symbol][interval] = signals
        return signals
    
    async def start_analysis(self, interval):
        await self.fetch_data_for_all_symbols(interval)
        
        results = []
        for symbol in self.symbols:
            signals = self.analyze_current_data(symbol, interval)
            if not signals:
                continue
            
            backtest_result = await self.backtester.backtest_strategy(symbol, interval)
            win_rate = backtest_result['winRate'] if backtest_result else 0
            total_tests = backtest_result['totalTests'] if backtest_result else 0
            
            prediction = signals['prediction']
            rsi = signals['indicators']['rsi']
            current_price = signals['indicators']['ma5']  # 使用MA5近似当前价格
            
            # 构建卡片文本
            card_text = [
                f"{symbol} 行情分析",
                f"{interval}",
                "趋势判断",
                f"{self.get_trend_arrow(prediction['direction'])} {'看涨' if prediction['direction'] == 'up' else '看跌' if prediction['direction'] == 'down' else '横盘'} ({int(prediction['confidence'] * 100)}%)",
                "回测胜率",
                f"{win_rate}%",
                f"基于{total_tests}次历史数据回测",
                "RSI指数",
                f"{rsi:.2f} ({self.get_rsi_level_description(rsi)})",
                "当前价格",
                f"${current_price:.2f}",
                "指标依据:",
                f"{self.get_macd_description(prediction['macdTrend'])}",
                "操作建议",
                f"{self.get_operation_suggestion(prediction)}"
            ]
            
            results.append("\n".join(card_text))
        
        return results


class Backtester:
    def __init__(self):
        self.historical_data = {}
        self.backtest_results = {}
    
    async def backtest_strategy(self, symbol, current_interval):
        analyzer = CryptoAnalyzer()
        await analyzer.fetch_data_for_all_symbols(current_interval)
        
        historical_data = analyzer.historical_data.get(symbol, {}).get(current_interval)
        if not historical_data or len(historical_data) < 100:
            return None
        
        current_signals = analyzer.calculate_signals(historical_data)
        if not current_signals or not current_signals['prediction']:
            return None
        
        return self.run_backtest(historical_data, current_signals, analyzer)
    
    def run_backtest(self, data, current_signals, analyzer, test_periods=200):
        max_periods = min(test_periods, len(data) - 100)
        signals = []
        correct_predictions = 0
        total_predictions = 0
        
        for i in range(100, len(data) - 1):
            if total_predictions >= max_periods:
                break
                
            test_data = data[:i+1]
            test_signals = analyzer.calculate_signals(test_data)
            
            if test_signals and self.is_similar_signal(test_signals['prediction'], current_signals['prediction']):
                total_predictions += 1
                actual_movement = 'up' if data[i+1]['close'] > data[i]['close'] else 'down'
                
                if test_signals['prediction']['direction'] == actual_movement:
                    correct_predictions += 1
                
                signals.append({
                    'time': data[i]['time'],
                    'prediction': test_signals['prediction']['direction'],
                    'actual': actual_movement,
                    'correct': test_signals['prediction']['direction'] == actual_movement
                })
        
        win_rate = round((correct_predictions / total_predictions) * 100) if total_predictions > 0 else 0
        
        return {
            'winRate': win_rate,
            'totalTests': total_predictions,
            'correctPredictions': correct_predictions,
            'signals': signals
        }
    
    def is_similar_signal(self, signal1, signal2):
        # 简化信号相似性检查
        return (signal1['direction'] == signal2['direction'] and
                abs(signal1['confidence'] - signal2['confidence']) <= 0.2 and
                abs(signal1['rsi'] - signal2['rsi']) <= 10)


if __name__ == "__main__":
    import asyncio
    
    # 选择分析周期：10m, 30m, 1h
    analysis_period = input("请选择分析周期 (10m, 30m, 1h): ") or "1h"
    
    analyzer = CryptoAnalyzer()
    results = asyncio.run(analyzer.start_analysis(analysis_period))
    
    for result in results:
        print(result)
        print("\n" + "-"*50 + "\n")